/**********************************************************************************************/
/* FBL_File_Mac.cpp                                                          				  */
/*                                                                       					  */
/* Copyright Paradigma, 1993-2006 											   			  	  */
/**********************************************************************************************/

#include <VShared/FBL/publ/File_OS/mac/FBL_File_Mac.h>

#include <VShared/FBL/publ/Utilities/FBL_Util_Path.h>

#include <VShared/FBL/publ/Location/FBL_Location_Disk_FSSpec.h>
#include <VShared/FBL/publ/Location/FBL_Location_FSRef.h>


/**********************************************************************************************/
#if FBL_MAC


/**********************************************************************************************/
FBL_Begin_Namespace


/**********************************************************************************************/
File_Mac::File_Mac( 
	OSType 			inCreator,
	OSType 			inFileType ) 
{
	mMacCreator		= inCreator;
	mMacType		= inFileType;
		
	mRefNum			= 0;  
	mReadOnly		= false;
}


/**********************************************************************************************/
File_Mac::~File_Mac( void ) 
{	
}


#pragma mark -
#pragma mark ===== I_Storage =====


/**********************************************************************************************/
// Forse to write on disk all cached information from this file.	
//
void File_Mac::Flush( void )
{
#if FBL_LENGTH <= 32	
	ParamBlockRec pb;
	pb.ioParam.ioRefNum = mRefNum;
	PBFlushFile( &pb, false );
#else	
	::FSFlushFork( mRefNum );
#endif	

}


#pragma mark -
#pragma mark ===== File =====


/**********************************************************************************************/
// Make direct reading from the disk file of inHowMuch bytes to the inBuffer.
// Returns inHowMuch bytes was actually readed to the buffer.
//
ulong File_Mac::DoRead( 
	char* 	inBuffer, 
	flength inFrom, 
	ulong 	inHowMuch ) 
{ 
	OSErr err = 0;

#if FBL_LENGTH <= 32	
	/* Set file marker to right position */
	err = SetFPos(mRefNum, fsFromStart, (long)inFrom);
	err = FSRead(mRefNum,(long*) &inHowMuch, inBuffer);
#else
	err = ::FSReadFork(mRefNum, fsFromStart, (llong)inFrom, inHowMuch, inBuffer, &inHowMuch );	
#endif	
	
	if( err )
		throw xOSFileError( (ERROR_TYPE)  err );

	return inHowMuch;	
}


/**********************************************************************************************/
// Write from inBuffer to the disk file inHowMuch bytes starting at inFrom position.
// Returns inHowMuch bytes was actually writen. 
//
ulong File_Mac::DoWrite( 
	const char* inBuffer, 
	flength 	inFrom, 
	ulong 		inHowMuch ) 
{ 
	if( mReadOnly )
		FBL_Throw(xOSFileError(ERR_OS_FILE_READ_ONLY));

	OSErr err = 0;

#if FBL_LENGTH <= 32	
	err = ::SetFPos(mRefNum, fsFromStart, (long) inFrom);
	if( err )
		throw xOSFileError( (ERROR_TYPE)  err );

	err = ::FSWrite(mRefNum, (long*)&inHowMuch, inBuffer);
	if( err )
		throw xOSFileError( (ERROR_TYPE)  err );

#else	
	err = ::FSWriteFork(mRefNum, fsFromStart, (llong)inFrom, inHowMuch, inBuffer, NULL);
	if( err )
		throw xOSFileError( (ERROR_TYPE)  err );
#endif	
	
	// was no error, so we have write down all required bytes.
	return inHowMuch;	
}


#pragma mark -


/**********************************************************************************************/
void File_Mac::DoCreate( void ) 
{ 
	const FSSpec* pSpec = GetSpec();

	if( pSpec )
	{
		OSErr err = ::FSpCreate( pSpec, mMacCreator, mMacType, smSystemScript );

		if( err )
			throw xOSFileError( (ERROR_TYPE)  err );
	}
	else
	{
		Location_Disk_FSRef_Ptr pLoc = fbl_dynamic_cast<Location_Disk_FSRef>(mpLocation);	
		if( pLoc )
		{
			const String& fname = pLoc->get_FileName();
			const UChar* pname = fname.c_str();
			const FSRef* parentRef = pLoc->get_ParentFSRef();

			// set creator/type:
			FInfo finfo = {0};
			finfo.fdCreator = mMacCreator;
			finfo.fdType = mMacType;
					
			FSCatalogInfo catInfo = {0};
			*(FInfo*)(&catInfo.finderInfo) = finfo; 

			FSRef newRef;
			OSStatus serr = FSCreateFileUnicode( 
								parentRef, 
								(ulong) fname.length(), (const UniChar *) pname, 
								kFSCatInfoFinderInfo, &catInfo, &newRef, NULL );
			
			if( serr )
				throw xOSFileError( (ERROR_TYPE)  serr );
			else
				pLoc->put_ObjectFSRef( &newRef );			
		}
	}

	// OS do not open file on create.
	Open();
}


/**********************************************************************************************/
void File_Mac::DoClose( void ) 
{ 
	// -----------------------------
	// in case this file is cached we need remove pages from cache.
	I_AspectHost* pHost = dcast<I_AspectHost*>( this );
	if( pHost )
		pHost->SendMessageToAll( 1 );

#if FBL_LENGTH <= 32	
	OSErr err = ::FSClose(mRefNum);
#else	
	OSErr err = ::FSCloseFork(mRefNum);
#endif	

	mRefNum = 0;
	if( err )
		throw xOSFileError( (ERROR_TYPE)  err );
}


/**********************************************************************************************/
// Get length of file from MacOS. File can be closed.
// 
flength File_Mac::DoGetLengthOfClosed( void ) const throw()
{
	CInfoPBRec cInfo;

	const FSSpec* ps = GetSpec();

	cInfo.hFileInfo.ioNamePtr 	= (StringPtr) ps->name;
	cInfo.hFileInfo.ioVRefNum 	= ps->vRefNum;
	cInfo.hFileInfo.ioFDirIndex = 0;
	cInfo.hFileInfo.ioDirID 	= ps->parID;
	
	OSErr err = PBGetCatInfoSync( &cInfo );
	argused1( err );

	return (flength) cInfo.hFileInfo.ioFlLgLen;

}


/**********************************************************************************************/
// Get length of file from MacOS, we know that file is opened. 
// 
flength File_Mac::DoGetLengthOfOpened( void ) const throw()
{
	flength Length = 0;

#if FBL_LENGTH <= 32	
	OSErr err = ::GetEOF( mRefNum, (long*) &Length );
#else	
	OSErr err = ::FSGetForkSize( mRefNum, (llong*) &Length );
#endif	

	argused1( err );

	return Length;
}


/**********************************************************************************************/
void File_Mac::DoOpen() 
{ 
	OSErr 		err;
	OSStatus 	errs;
	
	short newRefNum = 0;	
	mReadOnly = false;

	FSRef fsRef;

	// ----------------------------------
	const FSSpec* pSpec = GetSpec();
	if( pSpec ) // we have Location_FSSpec
	{
		err = ::FSpMakeFSRef( pSpec, &fsRef );
		if( err )
			throw xOSFileError( ERR_OS_FILE_NOT_FOUND, get_Name().c_str() );
	}
	else
	{
		Location_Disk_FSRef_Ptr pLoc = fbl_dynamic_cast<Location_Disk_FSRef>(mpLocation);	
		
		if( pLoc == NULL )
			throw xInternalError( ERR_INTERNAL_INVALID_PARAMETER, "open" );
		
		errs = pLoc->get_ObjectFSRef( &fsRef );
	}

	// ----------------------------------
	mReadOnly = bool( FSCheckLock( &fsRef ) != 0 );

	SInt8 perm = SInt8( mReadOnly ? fsRdPerm : fsCurPerm );

	HFSUniStr255 dfName;
	FSGetDataForkName( &dfName );

	err = ::FSOpenFork( &fsRef, dfName.length, dfName.unicode, perm, &newRefNum );
	
	if( err )
	{
		const UChar* pName = get_Name().c_str();
		throw xOSFileError( ERR_OS_FILE_NOT_FOUND, pName );
	}

	mRefNum = newRefNum;
}


/**********************************************************************************************/
void File_Mac::DoPutLength( flength inLength )
{
#if FBL_LENGTH <= 32	
	OSErr err = ::SetEOF( mRefNum, long(inLength) );	
#else	
	OSErr err = ::FSSetForkSize( mRefNum, fsFromStart, (llong) inLength );	
#endif	

	if( err )
		throw xOSFileError( (ERROR_TYPE)  err );
}


/**********************************************************************************************/
void File_Mac::DoThrowOut( void )
{
	const FSSpec* pSpec = GetSpec();

	if( pSpec )
	{
		OSErr err = ::FSpDelete( pSpec );
		if( err )
			throw xOSFileError( (ERROR_TYPE)  err );
	}
	else
	{
		FSRef fsRef;
		
		Location_Disk_FSRef_Ptr pLoc = fbl_dynamic_cast<Location_Disk_FSRef>(mpLocation);	
		
		OSStatus errs = pLoc->get_ObjectFSRef( &fsRef );
		argused1( errs );
	
		OSStatus serr = FSDeleteObject( &fsRef );	
		if( serr == 0 )
		{
			pLoc->put_ObjectFSRef( NULL );
		}		
	}
}


#pragma mark -
#pragma mark ===== File_Mac =====

 
 
/**********************************************************************************************/
const FSSpec* File_Mac::GetSpec( void ) const 
{ 
	if( mpLocation == NULL )
		return NULL;
	
	return (FSSpec*) mpLocation->get_FSSpec(); 
}


/**********************************************************************************************/
flength	File_Mac::GetPos( void ) const throw()	
{ 
	flength pos;

	OSErr err =  FSGetForkPosition( mRefNum, (llong*) &pos );
	argused1( err );

	return pos;
}


/**********************************************************************************************/
void File_Mac::SetPos( flength pos ) throw()	
{ 
	OSErr err = FSSetForkPosition( mRefNum, fsFromStart, (llong) pos );
	argused1( err );
}


/**********************************************************************************************/
FBL_End_Namespace

#endif // FBL_MAC



